#if DEF_CLIENT

using System;
using System.Collections.Generic;
using UnityEngine;

// 使其能在Inspector面板显示，并且可以被赋予相应值
[Serializable]
public class ReferenceCollectorData
{
    public string key;
    public UnityEngine.Object gameObject;
}

// 继承IComparer对比器，Ordinal会使用序号排序规则比较字符串，因为是byte级别的比较，所以准确性和性能都不错
public class ReferenceCollectorDataComparer : IComparer<ReferenceCollectorData>
{
    public int Compare(ReferenceCollectorData x, ReferenceCollectorData y)
    {
        return string.Compare(x.key, y.key, StringComparison.Ordinal);
    }
}

// 继承ISerializationCallbackReceiver后会增加OnAfterDeserialize和OnBeforeSerialize两个回调函数，如果有需要可以在对需要序列化的东西进行操作
// ET在这里主要是在OnAfterDeserialize回调函数中将data中存储的ReferenceCollectorData转换为dict中的Object，方便之后的使用
// 注意UNITY_EDITOR宏定义，在编译以后，部分编辑器相关函数并不存在
public class ReferenceCollector : MonoBehaviour, ISerializationCallbackReceiver
{
    // 用于序列化的List
    public List<ReferenceCollectorData> data = new();
    private readonly Dictionary<string, UnityEngine.Object> dict = new();

#if UNITY_EDITOR
    // 添加新的元素
    public void Add(string key, UnityEngine.Object obj)
    {
        UnityEditor.SerializedObject serializedObject = new(this);
        // 根据PropertyPath读取数据
        // 如果不知道具体的格式，可以右键用文本编辑器打开一个prefab文件（如Bundles/UI目录中的几个）
        // 因为这几个prefab挂载了ReferenceCollector，所以搜索data就能找到存储的数据
        UnityEditor.SerializedProperty dataProperty = serializedObject.FindProperty("data");
        int i;
        // 遍历data，看添加的数据是否存在相同key
        for (i = 0; i < data.Count; i++)
        {
            if (data[i].key == key)
            {
                break;
            }
        }
        // 不等于data.Count意为已经存在于data List中，直接赋值即可
        if (i != data.Count)
        {
            // 根据i的值获取dataProperty，也就是data中的对应ReferenceCollectorData，不过在这里，是对Property进行的读取，有点类似json或者xml的节点
            UnityEditor.SerializedProperty element = dataProperty.GetArrayElementAtIndex(i);
            // 对对应节点进行赋值，值为gameobject相对应的fileID
            // fileID独一无二，单对单关系，其他挂载在这个gameobject上的script或组件会保存相对应的fileID
            element.FindPropertyRelative("gameObject").objectReferenceValue = obj;
        }
        else
        {
            // 等于则说明key在data中无对应元素，所以得向其插入新的元素
            dataProperty.InsertArrayElementAtIndex(i);
            UnityEditor.SerializedProperty element = dataProperty.GetArrayElementAtIndex(i);
            element.FindPropertyRelative("key").stringValue = key;
            element.FindPropertyRelative("gameObject").objectReferenceValue = obj;
        }
        // 应用与更新
        UnityEditor.EditorUtility.SetDirty(this);
        serializedObject.ApplyModifiedProperties();
        serializedObject.UpdateIfRequiredOrScript();
    }

    // 删除元素
    public void Remove(string key)
    {
        UnityEditor.SerializedObject serializedObject = new(this);
        UnityEditor.SerializedProperty dataProperty = serializedObject.FindProperty("data");
        int i;
        for (i = 0; i < data.Count; i++)
        {
            if (data[i].key == key)
            {
                break;
            }
        }
        if (i != data.Count)
        {
            dataProperty.DeleteArrayElementAtIndex(i);
        }
        UnityEditor.EditorUtility.SetDirty(this);
        serializedObject.ApplyModifiedProperties();
        serializedObject.UpdateIfRequiredOrScript();
    }

    public void Clear()
    {
        UnityEditor.SerializedObject serializedObject = new(this);
        // 根据PropertyPath读取prefab文件中的数据
        // 如果不知道具体的格式，可以直接右键用文本编辑器打开，搜索data就能找到
        var dataProperty = serializedObject.FindProperty("data");
        dataProperty.ClearArray();
        UnityEditor.EditorUtility.SetDirty(this);
        serializedObject.ApplyModifiedProperties();
        serializedObject.UpdateIfRequiredOrScript();
    }

    public void Sort()
    {
        UnityEditor.SerializedObject serializedObject = new(this);
        data.Sort(new ReferenceCollectorDataComparer());
        UnityEditor.EditorUtility.SetDirty(this);
        serializedObject.ApplyModifiedProperties();
        serializedObject.UpdateIfRequiredOrScript();
    }
#endif

    public T Get<T>(string key) where T : class
    {
        if (!dict.TryGetValue(key, out var dictGo))
        {
            return null;
        }

        return dictGo as T;
    }

    public UnityEngine.Object GetObject(string key)
    {
        if (!dict.TryGetValue(key, out var dictGo))
        {
            return null;
        }

        return dictGo;
    }

    public void OnBeforeSerialize()
    {
    }

    public void OnAfterDeserialize()
    {
        dict.Clear();
        foreach (ReferenceCollectorData referenceCollectorData in data)
        {
            if (!dict.ContainsKey(referenceCollectorData.key))
            {
                dict.Add(referenceCollectorData.key, referenceCollectorData.gameObject);
            }
        }
    }
}

#endif